package gobase

import (
	"bytes"
	"fmt"
	"slices"
	"sort"
	"strconv"
	"strings"
)

// DifferenceSlice 使用切片表示集合，并返回 A - B 的结果
func DifferenceSlice[T any](A, B []T) []T {
	// 创建一个 map 来存储集合 B 的元素，以便快速查找
	setB := make(map[any]struct{})
	for _, item := range B {
		setB[item] = struct{}{}
	}

	// 遍历集合 A，并检查每个元素是否不在集合 B 中
	var result []T
	for _, item := range A {
		if _, found := setB[item]; !found {
			result = append(result, item)
		}
	}
	return result
}

func DeleteEleWithFunc[T any](lst []T, fn func(e T) (delflag bool)) []T {
	idx := 0
	l := len(lst)

	for idx < l {
		e := lst[idx]
		if fn(e) {
			lst = append(lst[:idx], lst[idx+1:]...)
			l--
		} else {
			idx++
		}
	}

	return lst
}

func DeleteEmpty(lst []string) []string {
	return DeleteEleWithFunc(lst, func(e string) (delflag bool) {
		return len(e) == 0
	})
}

// added: s1比s0 多的元素, removed: s1比s0 少的元素
func FindSortedListDifferences[T Ordered](s0, s1 []T) (added, removed []T) {
	i, j := 0, 0

	for i < len(s0) && j < len(s1) {
		if s0[i] < s1[j] {
			removed = append(removed, s0[i])
			i++
		} else if s0[i] > s1[j] {
			added = append(added, s1[j])
			j++
		} else {
			i++
			j++
		}
	}

	// Handle remaining elements in s0
	for i < len(s0) {
		removed = append(removed, s0[i])
		i++
	}

	// Handle remaining elements in s1
	for j < len(s1) {
		added = append(added, s1[j])
		j++
	}

	return added, removed
}

// added: s1比s0 多的元素, removed: s1比s0 少的元素
func FindListDifferences[T Ordered](s0, s1 []T) (added, removed []T) {

	sort.Slice(s0, func(i, j int) bool {
		return s0[i] < s0[j]
	})

	sort.Slice(s1, func(i, j int) bool {
		return s1[i] < s1[j]
	})

	return FindSortedListDifferences(s0, s1)
}

// added: s1比s0 多的元素, removed: s1比s0 少的元素
func FindListDifferences2[T Ordered](s0, s1 []T) (added, removed []T) {

	// Create maps to record the occurrence of each element
	count0 := make(map[T]int)
	count1 := make(map[T]int)

	// Record the occurrence of each element in s0
	for _, elem := range s0 {
		count0[elem]++
	}

	// Record the occurrence of each element in s1
	for _, elem := range s1 {
		count1[elem]++
	}

	// Find elements that are added in s1
	for elem, count := range count1 {
		if count0[elem] < count {
			for i := 0; i < count-count0[elem]; i++ {
				added = append(added, elem)
			}
		}
	}

	// Find elements that are removed from s0
	for elem, count := range count0 {
		if count1[elem] < count {
			for i := 0; i < count-count1[elem]; i++ {
				removed = append(removed, elem)
			}
		}
	}

	return added, removed
}

// find b(any ele) in a
// In general,faster than sorted
func IndexEle[T comparable](a, b []T) (idx int) {
	for _, elemB := range b {
		for i, elemA := range a {
			if elemA == elemB {
				return i
			}
		}
	}
	return -1
}

// find b(any ele) in a, a and b must be sorted
func IndexEleSorted[T Ordered](a, b []T) (idx int) {
	i, j := 0, 0
	for i < len(a) && j < len(b) {
		if a[i] == b[j] {
			return i
		} else if a[i] < b[j] {
			i++
		} else {
			j++
		}
	}
	return -1
}

func StrToFloatListEx[T Float](str string, sep string, errAppend bool, vIfErr T) (rval []T) {
	str = Trim(str)
	if len(str) == 0 {
		return nil
	}
	strs := strings.Split(str, sep)
	rval = make([]T, 0, len(strs))
	for i := 0; i < len(strs); i++ {
		s := Trim(strs[i])
		v, err := strconv.ParseFloat(s, 10)
		if err == nil {
			rval = append(rval, T(v))
		} else if errAppend {
			rval = append(rval, vIfErr)
		}
	}
	return
}

func StrToIntListEx[T Integer](str string, sep string, errAppend bool, vIfErr T) (rval []T) {
	str = Trim(str)
	if len(str) == 0 {
		return nil
	}
	strs := strings.Split(str, sep)
	rval = make([]T, 0, len(strs))
	for i := 0; i < len(strs); i++ {
		s := Trim(strs[i])
		v, err := strconv.ParseInt(s, 10, 64)
		if err == nil {
			rval = append(rval, T(v))
		} else if errAppend {
			rval = append(rval, vIfErr)
		}
	}
	return
}

func IndexSortedList[T Ordered](increasingSortedList []T, find T) int {
	if idx, ok := slices.BinarySearch(increasingSortedList, find); ok {
		return idx
	}
	return -1
}

func JoinMapBuf[K Ordered, V any](mp map[K]V, sep []byte, sortfn func(k1 K, v1 V, k2 K, v2 V) bool, joinFn func(k K, v V) []byte) []byte {
	type kv struct {
		k K
		v V
	}
	var lst = make([]*kv, 0, len(mp))
	for k, v := range mp {
		lst = append(lst, &kv{k: k, v: v})
	}

	if sortfn != nil {
		sort.Slice(lst, func(i, j int) bool {
			kv1 := lst[i]
			kv2 := lst[j]
			return sortfn(kv1.k, kv1.v, kv2.k, kv2.v)
		})
	} else {
		sort.Slice(lst, func(i, j int) bool {
			return lst[i].k < lst[j].k
		})
	}

	if joinFn == nil {
		joinFn = func(k K, v V) []byte {
			return []byte(fmt.Sprintf("%v%s%v", k, "=", v))
		}
	}
	var sb bytes.Buffer
	for _, itm := range lst {
		if sb.Len() > 0 && len(sep) > 0 {
			sb.Write(sep)
		}
		sb.Write(joinFn(itm.k, itm.v))
	}
	return sb.Bytes()
}

// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
	~int | ~int8 | ~int16 | ~int32 | ~int64
}

// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
	~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}

// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
	Signed | Unsigned
}

// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
	~float32 | ~float64
}

// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
	~complex64 | ~complex128
}

// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
	Integer | Float | ~string
}
